#include "workflownetgenerator.h"

#include "../documents/data/simgraphicdata.h"
#include "../editors/items/graphicitemcommon.h"
#include "../editors/items/swpsimactivitygraphicitem.h"
#include "../editors/items/swpdecisiongraphicitem.h"

#include <bool.h>
#include <boolproxy.h>
#include <colorset.h>
#include <dynamictypemanager.h>
#include <expressionengine.h>
#include <expressionvalidator.h>
#include <gdmodelquerymaker.h>
#include <iarc.h>
#include <iplace.h>
#include <itransition.h>
#include <multiset.h>
#include <multisetproxy.h>
#include <typefactory.h>
#include <wfnet.h>

#include <QtCore/QQueue>
#include <QDebug>
using namespace core;
using namespace swp;
using namespace wfnet;

typedef QPair<QHash<Node*, QUuid>, QHash<Node*, QUuid> > Connections;

WorkflowNetGenerator* workflowNetGenerator = 0;

WorkflowNetGenerator* WorkflowNetGenerator::getInstance()
{
    if (workflowNetGenerator == 0)
    {
        Bool boolean;

        if (!TypeFactory::getInstance()->isRegistered(boolean))
        {
            TypeFactory::getInstance()->registerType(boolean);
        }

        ExpressionEngine::getInstance()->registerTypeProxy(Bool::NAME,
            BoolProxy::NAME);
        ExpressionEngine::getInstance()->registerTypeProxy(Multiset::NAME,
            MultisetProxy::NAME);

        ExpressionValidator::getInstance()->registerKeywordMasks(
            TypeFactory::getInstance()->registeredKeywordMasks());

        workflowNetGenerator = new WorkflowNetGenerator();
    }

    return workflowNetGenerator;
}

void WorkflowNetGenerator::destroy()
{
    delete workflowNetGenerator;
    workflowNetGenerator = 0;
}

WfNet* WorkflowNetGenerator::generate(SimGraphicData *data)
{
    data->m_reportMapping.clear();

    WfNet* net = new WfNet(data);

    Bool boolean;

    DynamicTypeManager::getInstance()->registerColorSet(
        "Tokens", boolean, net);

    DynamicTypeManager::getInstance()->registerVariable(
        "i", "Tokens", net); // inner
    DynamicTypeManager::getInstance()->registerVariable(
        "o", "Tokens", net); // outer
    DynamicTypeManager::getInstance()->registerVariable(
        "d", "Tokens", net); // for random decision

    GDModelQueryMaker queryMaker(*data);

    const QList<IGraphicEditorItem* > sp = queryMaker.getItems(
        SWP_SIM_START_POINT_ITEM);
    const QList<IGraphicEditorItem* > ep = queryMaker.getItems(
        SWP_SIM_END_POINT_ITEM);
    if (sp.size() == 1 && ep.size() == 1)
    {
        traversing(sp.first(), ep.first(), data, net);
    }

    net->initialMarkingSet();

    return net;
}

void WorkflowNetGenerator::destroyNet(WfNet* net)
{
    DynamicTypeManager::getInstance()->unregisterVariable(
        "i", net); // inner

    DynamicTypeManager::getInstance()->unregisterVariable(
        "o", net); // outer

    DynamicTypeManager::getInstance()->unregisterVariable(
        "d", net); // decision

    DynamicTypeManager::getInstance()->unregisterColorSet(
        "Tokens", net);

    delete net;
}

WorkflowNetGenerator::WorkflowNetGenerator()
{

}

WorkflowNetGenerator::~WorkflowNetGenerator()
{

}

void WorkflowNetGenerator::traversing(IGraphicEditorItem* startPoint,
    IGraphicEditorItem* endPoint, SimGraphicData *data, WfNet* net)
{
    ColorSet tokens = *DynamicTypeManager::getInstance()->getColorSet(
        "Tokens", net);

    int splitCounter = 0;
    int joinCounter = 0;
    int decisionCounter = 0;
    int mergeCounter = 0;

    if (data->hasItem(startPoint->id()) &&
        data->hasItem(endPoint->id()))
    {
        QHash<Node*, Connections> nodeToConnectionsMapping;

        Node* startNode = data->getNode(startPoint);
        Node* endNode = data->getNode(endPoint);

        QQueue<Node* > queue;
        QSet<Node* > visited;

        // bfs traversing
        queue.enqueue(startNode);

        while (!queue.isEmpty())
        {
            Node* current = queue.dequeue();
            visited.insert(current);

            const QSet<Node* > outgoing =
                current->getOutgoingConnections();

            foreach (Node* to, outgoing)
            {
                if (!visited.contains(to) && !queue.contains(to))
                {
                    queue.enqueue(to);
                }
            }

            const IGraphicEditorItem* item =
                dynamic_cast<const IGraphicEditorItem* >(
                    current->getData());

            const QSet<Node* > incoming =
                current->getIncomingConnections();

            nodeToConnectionsMapping[current] = transformToNetPart(
                item, incoming, outgoing, net, data, &tokens, splitCounter,
                joinCounter, decisionCounter, mergeCounter);
        }

        queue.clear();
        visited.clear();

        queue.enqueue(startNode);

        while (!queue.isEmpty())
        {
            Node* current = queue.dequeue();
            visited.insert(current);

            const QSet<Node* > outgoing =
                current->getOutgoingConnections();

            foreach (Node* to, outgoing)
            {
                if (!visited.contains(to) && !queue.contains(to))
                {
                    queue.enqueue(to);
                }
            }

            const IGraphicEditorItem* item =
                dynamic_cast<const IGraphicEditorItem* >(
                    current->getData());

            const QSet<Node* > incoming =
                current->getIncomingConnections();

            connectNetPart(item, current, incoming, net, nodeToConnectionsMapping);
        }
    }
}

Connections WorkflowNetGenerator::transformToNetPart(
    const core::IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, WfNet* net, SimGraphicData* data, ColorSet* tokens,
    int& splitCounter, int& joinCounter,
    int& decisionCounter, int& mergeCounter)
{
    Connections ret;

    switch(item->type())
    {
        case SWP_SIM_ACTIVITY_ITEM:
            ret = transformToActivity(item, incoming, outgoing, net, data, tokens);
            break;
        case SWP_SIM_DECISION_ITEM:
            ret = transformToDecision(item, incoming, outgoing, net, tokens, decisionCounter);
            break;
        case SWP_SIM_JOIN_ITEM:
            ret = transformToJoin(item, incoming, outgoing, net, tokens, joinCounter);
            break;
        case SWP_SIM_SPLIT_ITEM:
            ret = transformToSplit(item, incoming, outgoing, net, tokens, splitCounter);
            break;
        case SWP_SIM_MERGE_ITEM:
            ret = transformToMerge(item, incoming, outgoing, net, tokens, mergeCounter);
            break;
        case SWP_SIM_START_POINT_ITEM:
            ret = transformToStartPoint(item, incoming, outgoing, net, tokens);
            break;
        case SWP_SIM_END_POINT_ITEM:
            ret = transformToEndPoint(item, incoming, outgoing, net, data, tokens);
            break;
        default:
            Q_ASSERT(false); // bad item in the model
    }

    return ret;
}

void WorkflowNetGenerator::connectNetPart(const IGraphicEditorItem* item, Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections>& connections)
{
    switch(item->type())
    {
        case SWP_SIM_ACTIVITY_ITEM:
            connectActivity(current, incoming, net, connections);
            break;
        case SWP_SIM_DECISION_ITEM:
            connectDecision(current, incoming, net, connections);
            break;
        case SWP_SIM_JOIN_ITEM:
            connectJoin(current, incoming, net, connections);
            break;
        case SWP_SIM_SPLIT_ITEM:
            connectSplit(current, incoming, net, connections);
            break;
        case SWP_SIM_MERGE_ITEM:
            connectMerge(current, incoming, net, connections);
            break;
        case SWP_SIM_START_POINT_ITEM:
            connectStartPoint(current, incoming, net, connections);
            break;
        case SWP_SIM_END_POINT_ITEM:
            connectEndPoint(current, incoming, net, connections);
            break;
        default:
            Q_ASSERT(false); // bad item in the model
    }
}

Connections WorkflowNetGenerator::transformToActivity(
    const IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, WfNet* net, SimGraphicData* data,
    ColorSet* tokens)
{
    Connections ret;

    //qDebug() << "Activity";

    const SwpSimActivityGraphicItem* _item =
        dynamic_cast<const SwpSimActivityGraphicItem* >(item);

    Q_ASSERT(item != 0); // can't happen

    IExpression* expression1 = net->addExpression("i", "Tokens");
    IExpression* expression2 = net->addExpression("i", "Tokens");

    IPlace* iPlace = net->addPlace(tokens,
        QString("%1_ip").arg(_item->propertyValue("name").toString()));

    ITransition* activity = net->addTransition(
        QString("%1_t").arg(_item->propertyValue("name").toString()));
    //qDebug() << "transition: " << QString("%1_t").arg(_item->propertyValue("name").toString());

    data->m_reportMapping[activity->getId()] = _item;

    IPlace* oPlace = net->addPlace(tokens,
        QString("%1_op").arg(_item->propertyValue("name").toString()));
    //qDebug() << "place: " << QString("%1_op").arg(_item->propertyValue("name").toString());

    IArc* connection1 = net->connect(iPlace, activity);
    connection1->setExpression(expression1);
    IArc* connection2 = net->connect(activity, oPlace);
    connection2->setExpression(expression2);

    //qDebug() << "\n";

    QHash<Node*, QUuid> map;

    foreach (Node* node, incoming)
    {
        map[node] = iPlace->getId();
    }

    ret.first = map;

    map.clear();

    foreach (Node* node, outgoing)
    {
        map[node] = oPlace->getId();
    }

    ret.second = map;

    return ret;
}

Connections WorkflowNetGenerator::transformToDecision(
    const core::IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, wfnet::WfNet* net,
    ColorSet* tokens, int& decisionCounter)
{
    Connections ret;

    //qDebug() << "Decision";

    IExpression* expressionIBr1 = net->addExpression("i", "Tokens");
    IExpression* expressionIBr2 = net->addExpression("i", "Tokens");
    IExpression* expressionOBr1 = net->addExpression("{1*true}", "Tokens");
    IExpression* expressionOBr2 = net->addExpression("{1*true}", "Tokens");

    IExpression* guard = net->addExpression("i == true", "Tokens");
    IExpression* antiGuard = net->addExpression("i != true", "Tokens");

    ITransition* transitionBr1 = net->addTransition(
        QString("d%1_t1").arg(++decisionCounter));
    transitionBr1->setGuard(guard);
    ITransition* transitionBr2 = net->addTransition(
        QString("d%1_t2").arg(decisionCounter));
    transitionBr2->setGuard(antiGuard);
    //qDebug() << "transition: " << QString("d%1_t1").arg(decisionCounter);
    //qDebug() << "transition: " << QString("d%1_t2").arg(decisionCounter);

    IPlace* iPlace1 = net->addPlace(tokens,
        QString("d%1_ip").arg(decisionCounter));
    //qDebug() << "place: " << QString("d%1_ip").arg(decisionCounter);

    IPlace* oPlace1 = net->addPlace(tokens,
        QString("d%1_op1").arg(decisionCounter));
    IPlace* oPlace2 = net->addPlace(tokens,
        QString("d%1_op2").arg(decisionCounter));
    //qDebug() << "place: " << QString("d%1_op1").arg(decisionCounter);
    //qDebug() << "place: " << QString("d%1_op2").arg(decisionCounter);

    IArc* connection1 = net->connect(iPlace1, transitionBr1);
    connection1->setExpression(expressionIBr1);
    IArc* connection2 = net->connect(iPlace1, transitionBr2);
    connection2->setExpression(expressionIBr2);

    IArc* connection3 = net->connect(transitionBr1, oPlace1);
    connection3->setExpression(expressionOBr1);
    IArc* connection4 = net->connect(transitionBr2, oPlace2);
    connection4->setExpression(expressionOBr2);

    QHash<Node*, QUuid> map;

    foreach (Node* node, incoming)
    {
        map[node] = iPlace1->getId();
    }

    ret.first = map;

    map.clear();

    const SwpDecisionGraphicItem* _item = dynamic_cast<const SwpDecisionGraphicItem*>(item);

    Q_ASSERT(_item != 0); // can't happen

    Q_ASSERT(outgoing.size() == 2); // can't happen

    QList<Node*> _outgoing = outgoing.toList();

    if (_outgoing[0]->getData() == dynamic_cast<const INodeData*>(_item->yesBranchItem()))
    {
        map[_outgoing[0]] = oPlace1->getId();
        map[_outgoing[1]] = oPlace2->getId();
    }
    else
    {
        map[_outgoing[1]] = oPlace1->getId();
        map[_outgoing[0]] = oPlace2->getId();
    }

    ret.second = map;

    //qDebug() << "\n";

    return ret;
}

Connections WorkflowNetGenerator::transformToJoin(
    const core::IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, wfnet::WfNet* net,
    ColorSet* tokens, int& joinCounter)
{
    Connections ret;

    //qDebug() << "Join";

    IExpression* expressionBr1 = net->addExpression("i", "Tokens");
    IExpression* expressionBr2 = net->addExpression("i", "Tokens");
    IExpression* expressionO = net->addExpression("i", "Tokens");

    ITransition* join = net->addTransition(
        QString("j%1_t").arg(++joinCounter));
    //qDebug() << "transition: " << QString("j%1_t").arg(joinCounter);

    IPlace* iPlace1 = net->addPlace(tokens,
        QString("j%1_ip1").arg(joinCounter));
    //qDebug() << "place: " << QString("j%1_ip1").arg(joinCounter);
    IPlace* iPlace2 = net->addPlace(tokens,
        QString("j%1_ip2").arg(joinCounter));
    //qDebug() << "place: " << QString("j%1_ip2").arg(joinCounter);

    IPlace* oPlace = net->addPlace(tokens,
        QString("j%1_op").arg(joinCounter));
    //qDebug() << "place: " << QString("j%1_op").arg(joinCounter);

    IArc* connection1 = net->connect(iPlace1, join);
    connection1->setExpression(expressionBr1);

    IArc* connection2 = net->connect(iPlace2, join);
    connection2->setExpression(expressionBr2);

    IArc* connection3 = net->connect(join, oPlace);
    connection3->setExpression(expressionO);

    QHash<Node*, QUuid> map;

    Q_ASSERT(incoming.size() == 2);

    QList<Node*> _incoming = incoming.toList();

    map[_incoming[0]] = iPlace1->getId();
    map[_incoming[1]] = iPlace2->getId();

    ret.first = map;

    map.clear();

    foreach (Node* node, outgoing)
    {
        map[node] = oPlace->getId();
    }

    ret.second = map;

    //qDebug() << "\n";

    return ret;
}

Connections WorkflowNetGenerator::transformToSplit(
    const core::IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, wfnet::WfNet* net,
    ColorSet* tokens, int& splitCounter)
{
    Connections ret;

    //qDebug() << "Split";

    IExpression* expressionBr1 = net->addExpression("i", "Tokens");
    IExpression* expressionBr2 = net->addExpression("i", "Tokens");

    ITransition* split = net->addTransition(
        QString("s%1_t").arg(++splitCounter));
    //qDebug() << "transition: " << QString("s%1_t").arg(splitCounter);

    IPlace* oPlace1 = net->addPlace(tokens,
        QString("s%1_op1").arg(splitCounter));
    //qDebug() << "place: " << QString("s%1_op1").arg(splitCounter);
    IPlace* oPlace2 = net->addPlace(tokens,
        QString("s%1_op2").arg(splitCounter));
    //qDebug() << "place: " << QString("s%1_op2").arg(splitCounter);

    IArc* connection1 = net->connect(split, oPlace1);
    connection1->setExpression(expressionBr1);

    IArc* connection2 = net->connect(split, oPlace2);
    connection2->setExpression(expressionBr2);

    /*
    foreach (IPlace* place, connectPlaces)
    {
        IExpression* _expression = net->addExpression("o", "Tokens");

        IArc* _connection = net->connect(place, split);
        _connection->setExpression(_expression);
    }*/

    QHash<Node*, QUuid> map;

    foreach (Node* node, incoming)
    {
        map[node] = split->getId();
    }

    ret.first = map;

    map.clear();

    Q_ASSERT(outgoing.size() == 2);

    QList<Node*> _outgoing = outgoing.toList();

    map[_outgoing[0]] = oPlace1->getId();
    map[_outgoing[1]] = oPlace2->getId();

    ret.second = map;

    //qDebug() << "\n";

    return ret;
}

Connections WorkflowNetGenerator::transformToMerge(
    const core::IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, wfnet::WfNet* net,
    ColorSet* tokens, int& mergeCounter)
{
    Connections ret;

    //qDebug() << "Merge";

    IExpression* expressionBr1 = net->addExpression("i", "Tokens");
    IExpression* expressionBr2 = net->addExpression("i", "Tokens");
    IExpression* expressionO1 = net->addExpression("i", "Tokens");
    IExpression* expressionO2 = net->addExpression("i", "Tokens");

    ITransition* merge1 = net->addTransition(
        QString("m%1_t1").arg(++mergeCounter));
    //qDebug() << "transition: " << QString("m%1_t1").arg(mergeCounter);

    ITransition* merge2 = net->addTransition(
        QString("m%1_t2").arg(mergeCounter));
    //qDebug() << "transition: " << QString("m%1_t2").arg(mergeCounter);

    IPlace* iPlace1 = net->addPlace(tokens,
        QString("m%1_ip1").arg(mergeCounter));
    //qDebug() << "place: " << QString("j%1_ip1").arg(mergeCounter);
    IPlace* iPlace2 = net->addPlace(tokens,
        QString("m%1_ip2").arg(mergeCounter));
    //qDebug() << "place: " << QString("j%1_ip2").arg(mergeCounter);

    IPlace* oPlace = net->addPlace(tokens,
        QString("m%1_op").arg(mergeCounter));
    //qDebug() << "place: " << QString("j%1_op").arg(mergeCounter);

    IArc* connection1 = net->connect(iPlace1, merge1);
    connection1->setExpression(expressionBr1);

    IArc* connection2 = net->connect(iPlace2, merge2);
    connection2->setExpression(expressionBr2);

    IArc* connection3 = net->connect(merge1, oPlace);
    connection3->setExpression(expressionO1);
    IArc* connection4 = net->connect(merge2, oPlace);
    connection4->setExpression(expressionO2);

    QHash<Node*, QUuid> map;

    Q_ASSERT(incoming.size() == 2);

    QList<Node*> _incoming = incoming.toList();

    map[_incoming[0]] = iPlace1->getId();
    map[_incoming[1]] = iPlace2->getId();

    ret.first = map;

    map.clear();

    foreach (Node* node, outgoing)
    {
        map[node] = oPlace->getId();
    }

    ret.second = map;

    //qDebug() << "\n";

    return ret;

}

Connections WorkflowNetGenerator::transformToStartPoint(
    const core::IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, wfnet::WfNet* net,
    ColorSet* tokens)
{
    Connections ret;

    //qDebug() << "Start point";

    Multiset ms("Tokens", "{1*true}");

    IPlace* startPlace = net->addPlace(tokens, "sp");
    startPlace->putTokens(&ms);
    //qDebug() << "place: sp";

    QHash<Node*, QUuid> map;

    foreach (Node* node, outgoing)
    {
        map[node] = startPlace->getId();
    }

    ret.second = map;

    //qDebug() << "\n";

    return ret;
}

Connections WorkflowNetGenerator::transformToEndPoint(
    const core::IGraphicEditorItem* item, const QSet<Node* >& incoming, const QSet<Node* >& outgoing, wfnet::WfNet* net, SimGraphicData* data,
    ColorSet* tokens)
{
    Connections ret;

    //qDebug() << "End point";

    IPlace* endPlace = net->addPlace(tokens, "ep");
    //qDebug() << "place: ep";

    data->m_endPlace = endPlace->getId();

    /*
    int tempConnectionCounter = 0;

    foreach (IPlace* place, connectPlaces)
    {
        IExpression* _expression1 = net->addExpression("o", "Tokens");
        IExpression* _expression2 = net->addExpression("o", "Tokens");

        ITransition* temp = net->addTransition(
            QString("ep_tt%1").arg(++tempConnectionCounter));
        //qDebug() << "transition: " << QString("ep_tt%1").arg(tempConnectionCounter);

        IArc* _connection1 = net->connect(place, temp);
        _connection1->setExpression(_expression1);
        IArc* _connection2 = net->connect(temp, endPlace);
        _connection2->setExpression(_expression2);
    }
    */

    QHash<Node*, QUuid> map;

    foreach (Node* node, incoming)
    {
        map[node] = endPlace->getId();
    }

    ret.first = map;

    //qDebug() << "\n";

    return ret;
}

void WorkflowNetGenerator::connectActivity(Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections >& connections)
{
    //qDebug() << "Connection to activity";

    Q_ASSERT(connections.contains(current)); // can't happen

    const Connections& nodeCons = connections[current];

    int tempConnectionCounter = 0;

    foreach (Node* node, incoming)
    {
        Q_ASSERT(connections.contains(node)); // can't happen

        const Connections& cons = connections[node];

        Q_ASSERT(nodeCons.first.contains(node)); // can't happen
        Q_ASSERT(cons.second.contains(current)); // can't happen

        const QUuid iId = cons.second[current];

        const QUuid cId = nodeCons.first[node];

        IPlace* iPlace = net->place(iId);
        IPlace* cPlace = net->place(cId);

        Q_ASSERT(iPlace != 0 && cPlace != 0); // can't happen

        IExpression* expr1 = net->addExpression("o", "Tokens");
        IExpression* expr2 = net->addExpression("o", "Tokens");

        const QString name = cPlace->name().split("_", QString::SkipEmptyParts).first();

        ITransition* temp = net->addTransition(
            QString("%1_tt%2").arg(name).arg(++tempConnectionCounter));
        //qDebug() << "Transition: " << temp->name();

        IArc* arc1 = net->connect(iPlace, temp);
        arc1->setExpression(expr1);
        IArc* arc2 = net->connect(temp, cPlace);
        arc2->setExpression(expr2);
        //qDebug() << "Arc: " << iPlace->name() << cPlace->name();
    }

    //qDebug() << "\n";
}

void WorkflowNetGenerator::connectDecision(Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections >& connections)
{
    //qDebug() << "Connection to decision";

    Q_ASSERT(connections.contains(current)); // can't happen

    const Connections& nodeCons = connections[current];

    int tempConnectionCounter = 0;

    foreach (Node* node, incoming)
    {
        Q_ASSERT(connections.contains(node)); // can't happen

        const Connections& cons = connections[node];

        Q_ASSERT(nodeCons.first.contains(node)); // can't happen
        Q_ASSERT(cons.second.contains(current)); // can't happen

        const QUuid iId = cons.second[current];

        const QUuid cId = nodeCons.first[node];

        IPlace* iPlace = net->place(iId);
        IPlace* cPlace = net->place(cId);

        const QString name = cPlace->name().split("_", QString::SkipEmptyParts).first();

        Q_ASSERT(iPlace != 0 && cPlace != 0); // can't happen

        IExpression* expr1 = net->addExpression("o", "Tokens");
        IExpression* expr2 = net->addExpression("d", "Tokens");

        ITransition* temp = net->addTransition(
            QString("%1_tt%2").arg(name).arg(++tempConnectionCounter));
        //qDebug() << "Transition: " << temp->name();

        IArc* con1 = net->connect(iPlace, temp);
        con1->setExpression(expr1);
        IArc* con2 = net->connect(temp, cPlace);
        con2->setExpression(expr2);
    }

    //qDebug() << "\n";
}

void WorkflowNetGenerator::connectJoin(Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections >& connections)
{
    //qDebug() << "Connection to join";

    Q_ASSERT(connections.contains(current)); // can't happen

    const Connections& nodeCons = connections[current];

    int tempConnectionCounter = 0;

    foreach (Node* node, incoming)
    {
        Q_ASSERT(connections.contains(node)); // can't happen

        const Connections& cons = connections[node];

        Q_ASSERT(nodeCons.first.contains(node)); // can't happen
        Q_ASSERT(cons.second.contains(current)); // can't happen

        const QUuid iId = cons.second[current];

        const QUuid cId = nodeCons.first[node];

        IPlace* iPlace = net->place(iId);
        IPlace* cPlace = net->place(cId);

        const QString name = cPlace->name().split("_", QString::SkipEmptyParts).first();

        Q_ASSERT(iPlace != 0 && cPlace != 0); // can't happen

        IExpression* expr1 = net->addExpression("o", "Tokens");
        IExpression* expr2 = net->addExpression("o", "Tokens");

        ITransition* temp = net->addTransition(
            QString("%1_tt%2").arg(name).arg(++tempConnectionCounter));
        //qDebug() << "Transition: " << temp->getId();

        IArc* con1 = net->connect(iPlace, temp);
        con1->setExpression(expr1);
        IArc* con2 = net->connect(temp, cPlace);
        con2->setExpression(expr2);
    }

    //qDebug() << "\n";
}

void WorkflowNetGenerator::connectSplit(Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections >& connections)
{
    //qDebug() << "Connection to split";

    Q_ASSERT(connections.contains(current)); // can't happen

    const Connections& nodeCons = connections[current];

    foreach (Node* node, incoming)
    {
        Q_ASSERT(connections.contains(node)); // can't happen

        const Connections& cons = connections[node];

        Q_ASSERT(nodeCons.first.contains(node)); // can't happen
        Q_ASSERT(cons.second.contains(current)); // can't happen

        const QUuid iId = cons.second[current];

        const QUuid cId = nodeCons.first[node];

        IPlace* iPlace = net->place(iId);
        ITransition* cTransition = net->transition(cId);

        Q_ASSERT(iPlace != 0 && cTransition != 0); // can't happen

        IExpression* expr = net->addExpression("o", "Tokens");

        IArc* arc = net->connect(iPlace, cTransition);
        arc->setExpression(expr);
        //qDebug() << "Arc";
    }

    //qDebug() << "\n";
}

void WorkflowNetGenerator::connectMerge(Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections >& connections)
{
    //qDebug() << "Connection to merge";

    Q_ASSERT(connections.contains(current)); // can't happen

    const Connections& nodeCons = connections[current];

    int tempConnectionCounter = 0;

    foreach (Node* node, incoming)
    {
        Q_ASSERT(connections.contains(node)); // can't happen

        const Connections& cons = connections[node];

        Q_ASSERT(nodeCons.first.contains(node)); // can't happen
        Q_ASSERT(cons.second.contains(current)); // can't happen

        const QUuid iId = cons.second[current];

        const QUuid cId = nodeCons.first[node];

        IPlace* iPlace = net->place(iId);
        IPlace* cPlace = net->place(cId);

        const QString name = cPlace->name().split("_", QString::SkipEmptyParts).first();

        Q_ASSERT(iPlace != 0 && cPlace != 0); // can't happen

        IExpression* expr1 = net->addExpression("o", "Tokens");
        IExpression* expr2 = net->addExpression("o", "Tokens");

        ITransition* temp = net->addTransition(
            QString("%1_tt%2").arg(name).arg(++tempConnectionCounter));
        //qDebug() << "Transition: " << temp->getId();

        IArc* con1 = net->connect(iPlace, temp);
        con1->setExpression(expr1);
        IArc* con2 = net->connect(temp, cPlace);
        con2->setExpression(expr2);
    }

    //qDebug() << "\n";
}

void WorkflowNetGenerator::connectStartPoint(Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections >& connections)
{
    // do nothing
}

void WorkflowNetGenerator::connectEndPoint(Node* current, const QSet<Node* >& incoming, WfNet* net, const QHash<Node*, Connections >& connections)
{
    //qDebug() << "Connection to end point";

    Q_ASSERT(connections.contains(current)); // can't happen

    const Connections& nodeCons = connections[current];

    int tempConnectionCounter = 0;

    foreach (Node* node, incoming)
    {
        Q_ASSERT(connections.contains(node)); // can't happen

        const Connections& cons = connections[node];

        Q_ASSERT(nodeCons.first.contains(node)); // can't happen
        Q_ASSERT(cons.second.contains(current)); // can't happen

        const QUuid iId = cons.second[current];

        const QUuid cId = nodeCons.first[node];

        IPlace* iPlace = net->place(iId);
        IPlace* cPlace = net->place(cId);

        Q_ASSERT(iPlace != 0 && cPlace != 0); // can't happen

        IExpression* expr1 = net->addExpression("o", "Tokens");
        IExpression* expr2 = net->addExpression("o", "Tokens");

        ITransition* temp = net->addTransition(
            QString("ep_tt%1").arg(++tempConnectionCounter));
        //qDebug() << "transition: " << temp->name();

        IArc* con1 = net->connect(iPlace, temp);
        con1->setExpression(expr1);
        IArc* con2 = net->connect(temp, cPlace);
        con2->setExpression(expr2);
    }

    //qDebug() << "\n";
}

